//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "CompactMotifSequence.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fstream>
#include <iostream>
#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct MotifCategory {
    
};

typedef struct MotifCategory MotifCategory;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 5000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CompactMotifSequence::CompactMotifSequence() {
  this->filenameFASTA   = new String();
  this->filenameMotif   = new String();
  this->filenameListRES = new String();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CompactMotifSequence::CompactMotifSequence(const CompactMotifSequence& compactMotifSequence) {
  this->filenameFASTA   = new String(compactMotifSequence.filenameFASTA);
  this->filenameMotif   = new String(compactMotifSequence.filenameMotif);
  this->filenameListRES = new String(compactMotifSequence.filenameListRES);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CompactMotifSequence::CompactMotifSequence(const CompactMotifSequence* compactMotifSequence) {
  this->filenameFASTA   = new String(compactMotifSequence->filenameFASTA);
  this->filenameMotif   = new String(compactMotifSequence->filenameMotif);
  this->filenameListRES = new String(compactMotifSequence->filenameListRES);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CompactMotifSequence::~CompactMotifSequence() {
  if (this->filenameFASTA)   delete this->filenameFASTA;
  if (this->filenameMotif)   delete this->filenameMotif;
  if (this->filenameListRES) delete this->filenameListRES;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CompactMotifSequence::FilenameFASTA(String* filenameFASTA) {
  if (filenameFASTA) {
    if (this->filenameFASTA) delete this->filenameFASTA;
    this->filenameFASTA = filenameFASTA;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CompactMotifSequence::FilenameMotif(String* filenameMotif) {
  if (filenameMotif) {
    if (this->filenameMotif) delete this->filenameMotif;
    this->filenameMotif = filenameMotif;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CompactMotifSequence::FilenameListRES(String* filenameListRES) {
  if (filenameListRES) {
    if (this->filenameListRES) delete this->filenameListRES;
    this->filenameListRES = filenameListRES;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CompactMotifSequence::LocationDB(String* locationDB) {
  if (locationDB) {
    if (this->locationDB) delete this->locationDB;
    this->locationDB = locationDB;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* CompactMotifSequence::FilenameFASTA() {
  return this->filenameFASTA;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* CompactMotifSequence::FilenameMotif() {
  return this->filenameMotif;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* CompactMotifSequence::FilenameListRES() {
  return this->filenameListRES;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* CompactMotifSequence::LocationDB() {
  return this->locationDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool CompactMotifSequence::ConstructReduceBindingSiteMotif(String* filenameOutSeq, String* filenameOutMot, String* filenameError) {
 
  if (!this->filenameFASTA || !this->filenameMotif || !this->filenameListRES || 
      !this->locationDB    || !filenameOutSeq      || !filenameOutMot        ||
      !filenameError)
    return false;

  fstream fileFASTA;
  fstream fileMotif;
  fstream fileRES;

  fstream fileOutSeq;
  fstream fileOutMot;
  fstream fileError;

  fileRES.open(this->filenameListRES->Out(), fstream::in);

  fileOutSeq.open(filenameOutSeq->Out(),     fstream::out);
  fileOutMot.open(filenameOutMot->Out(),     fstream::out);
  fileError.open(filenameError->Out(),       fstream::out);

  char            buffer   [LENGTHLINE + 1], bufferPrevious [LENGTHLINE + 1];
  char            bufferM  [LENGTHLINE + 1], bufferMPrevious[LENGTHLINE + 1];
  char            bufferS  [LENGTHLINE + 1], bufferSPrevious[LENGTHLINE + 1];
  char            bufferPDB[LENGTHLINE + 1];

  TListE <String>* listResidues = new TListE <String> ();
  TListE <String>* listIDE      = new TListE <String> ();

  String*          lineResidues = new String();
  String*          ident        = new String();

  bufferPrevious[0] = '\0';

  while (!fileRES.eof()) {

    fileRES.getline(buffer, LENGTHLINE);

    if (bufferPrevious[0] == '>') {

      lineResidues->In(buffer);
      lineResidues->RTrim();

      if (lineResidues->Length() > 2) {

        listIDE->Clear();
        this->SplitColumn(bufferPrevious, listIDE, "_\n\r");

        if (listIDE->Length()) {

          bool findMotif = false, findSequence = false;

          bufferMPrevious[0] = '\0';
          bufferSPrevious[0] = '\0';

          listIDE->Get(0)->In(' ', 0);
          listIDE->Get(0)->RTrim();
          
          fileMotif.open(this->filenameMotif->Out(), fstream::in);
          while (!fileMotif.eof()) {
            fileMotif.getline(bufferM, LENGTHLINE);
            if (strstr(bufferMPrevious, listIDE->Get(0)->Out())) {
              findMotif = true;
              break;
            }
            strcpy(bufferMPrevious, bufferM);
          }
          fileMotif.close();

          fileFASTA.open(this->filenameFASTA->Out(), fstream::in);
          while (!fileFASTA.eof()) {
            fileFASTA.getline(bufferS, LENGTHLINE);
            if (strstr(bufferSPrevious, listIDE->Get(0)->Out())) {
              findSequence = true;
              break;
            }
            strcpy(bufferSPrevious, bufferS);
          }
          fileFASTA.close();

          char locationPath[strlen(this->locationDB->Out()) + 100];
          sprintf(locationPath, "%spdb%s.ent.gz\0", this->locationDB->Out(), listIDE->Get(0)->Out());

          gzFile pdbFile      = gzopen(locationPath, "rb9");

          bool   condDBREF    = false;
          int    realPosition = -10000;

          String* linePDB     = new String();

          while (!gzeof(pdbFile)) {

            gzgets(pdbFile, bufferPDB, LENGTHLINE);

            if (!strncmp(bufferPDB, "DBREF", strlen("DBREF"))    &&
                 strncmp(bufferPDB, "DBREF2", strlen("DBREF2"))) {

              linePDB->In(bufferPDB);

              String* idPDB     = linePDB->SubStr(7, 11);
              String* chainPDB  = linePDB->SubStr(12, 13);
              String* posIniPDB = linePDB->SubStr(14, 18);

              listIDE->Get(0)->ToLower();
              idPDB->ToLower();
              
              condDBREF = true;

              if (!strcmp(listIDE->Get(1)->Out(), chainPDB->Out()) &&
                  !strcmp(listIDE->Get(0)->Out(), idPDB->Out())) {

                realPosition = atoi(posIniPDB->Out());

                if (idPDB)     delete idPDB;
                if (chainPDB)  delete chainPDB;
                if (posIniPDB) delete posIniPDB;

                break;

              }

              if (idPDB)     delete idPDB;
              if (chainPDB)  delete chainPDB;
              if (posIniPDB) delete posIniPDB;

            }
            else if (strncmp(bufferPDB, "DBREF", strlen("DBREF")) && condDBREF) 
            break;
            
          }

          gzclose(pdbFile);

          if (linePDB) delete linePDB;

          if (findMotif && findSequence && realPosition != 10000) {

            listResidues->Clear();
            this->SplitColumn(buffer, listResidues, ", \n\r");

            if (listResidues->Length()) {
              
              String*          compactMotif = new String();
              String*          compactSeq   = new String();
              TListE <String>* listPart     = new TListE <String> ();

              listResidues->SetInitial();
              while (listResidues->SetNext()) {

                listPart->Clear();
                this->SplitColumn(listResidues->GetCurrent()->Out(), listPart, "_ \n\r");

                if (listPart->Length()) {

                  listPart->Get(0)->RTrim();
                  listPart->Get(1)->RTrim();

                  int positionSearch = atoi(listPart->Get(1)->Out());

                  if      (realPosition > 0) positionSearch -= realPosition;
                  else if (realPosition < 0) positionSearch -= (realPosition + 1);
                  else                       positionSearch  = -1;

                  if (positionSearch == -1) {
                    compactMotif->In("");
                    compactSeq->In("");
                    break;
                  }

                  if (listPart->Get(0)->Out()[0] == bufferS[positionSearch]) {
                    for (int i = positionSearch - 2; i <= (positionSearch + 2); i++) 
                      if ((i >= 0) && (i < strlen(bufferS))) {
                        compactSeq->Add(bufferS[i]);
                        compactMotif->Add(bufferM[i]);
                      }
                  }
                  else {
                    compactMotif->In("");
                    compactSeq->In("");
                    break;
                  }

                }

              }

              if (compactMotif->Length()) {

                fileOutSeq << ">" << listIDE->Get(0)->Out() << "_" << listIDE->Get(1)->Out() << endl;
                fileOutSeq << compactSeq->Out() << endl;
                fileOutMot << ">" << listIDE->Get(0)->Out() << "_" << listIDE->Get(1)->Out() << endl;
                fileOutMot << compactMotif->Out() << endl;

                cout << ">" << listIDE->Get(0)->Out() << "_" << listIDE->Get(1)->Out() << endl;
                cout << compactSeq->Out() << endl;
                cout << ">" << listIDE->Get(0)->Out() << "_" << listIDE->Get(1)->Out() << endl;
                cout << compactMotif->Out() << endl;
                  
              }
              else fileError << bufferPrevious << endl;

              if (compactMotif) delete compactMotif;
              if (compactSeq)   delete compactSeq;
              if (listPart)     delete listPart;

            }
            else fileError << bufferPrevious << endl;

          }
          else fileError << bufferPrevious << endl;
          
        }
        else fileError << bufferPrevious << endl;

      }
      else fileError << bufferPrevious << endl;
        
    }

    strcpy(bufferPrevious, buffer);
      
  }

  if (listIDE)      delete listIDE;
  if (listResidues) delete listResidues;

  if (lineResidues) delete lineResidues;
  if (ident)        delete ident;

  fileRES.close();

  fileOutSeq.close();
  fileOutMot.close();
  fileError.close();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool CompactMotifSequence::SplitColumn(char* buffer, TListE<String>* columns, char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool CompactMotifSequence::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
